Skip to content

feat(exporter): Prisma ORM exporter 추가#151

Open
L33gn21 wants to merge 4 commits into
dev-five-git:mainfrom
L33gn21:feat/prisma-exporter
Open

feat(exporter): Prisma ORM exporter 추가#151
L33gn21 wants to merge 4 commits into
dev-five-git:mainfrom
L33gn21:feat/prisma-exporter

Conversation

@L33gn21
Copy link
Copy Markdown

@L33gn21 L33gn21 commented May 23, 2026

개요

5번째 ORM 백엔드로 Prisma를 추가합니다 (기존: SeaORM / SQLAlchemy / SQLModel / JPA).
TableDef 스키마를 datasource, generator, enum 블록(전역 dedup), model 블록을
포함한 단일 schema.prisma 파일로 변환합니다.

실행:

vespertide export --orm prisma

설계상 특이사항

1. 왜 Prisma만 별도 config (PrismaConfig)를 가지는가

Prisma는 단일 스키마 언어입니다. datasource db { provider, url, relationMode }
generator client { provider, output }이 모델과 같은 파일 안에 있어야 합니다.
나머지 ORM 백엔드(SeaORM / SQLAlchemy / SQLModel / JPA)는 모델 단위 코드만
생성하므로 이런 메타 정보가 필요 없습니다. 즉, Prisma만 "모델 외 입력"(provider,
client output 경로, relation mode)을 받아야 하기 때문에 vespertide-config
PrismaConfig를 새로 추가했습니다. 필드 세 개(provider / client_output /
relation_mode)와 postgresql 기본값을 가집니다.

2. 왜 export.rsif matches!(orm, OrmArg::Prisma) { return cmd_export_prisma(...) } 분기가 있는가

기존 export 파이프라인은 모델 N개 → 파일 N개 전제입니다:
walk_models → 모델별 render_entity_with_schema → 파일별 write → mod chain.
Prisma는 모델 N개 → 파일 1개이며 위 파이프라인이 만족시킬 수 없는 두 가지
요구가 있습니다:

  • 모든 테이블에 걸친 enum 전역 dedup (두 테이블이 같은 enum을 참조해도 enum
    블록은 파일에 한 번만 등장해야 함)
  • datasource/generator 헤더가 파일 맨 위에 1회만 출력되어야 함

그래서 cmd_export가 진입 즉시 cmd_export_prisma로 분기하고,
PrismaExporterWithConfig::render_schema(&all_tables)에서 스키마 전체를 한 번에
조립합니다. Orm::Prisma는 인터페이스 일관성 + 분기 안 자체 clean 호출을 위해
clean_export_dir / build_output_pathprisma 확장자로 함께 등록만 해
두었습니다.

테스트 설계

명세는 crates/vespertide-exporter/src/prisma/TESTING.md에 있습니다. 3-layer 구조:

  • Layer 1 — FK 없는 단일 엔티티 렌더링. render_entity(table)로 검증.
  • Layer 2 — schema 컨텍스트가 필요한 관계 포함 케이스.
    render_entity_with_schema(table, schema)로 검증. TableConstraint::ForeignKey
    하나라도 있는 TableDef는 무조건 Layer 2.
  • Layer 3 — edge case (예약어, 특수문자).

설계 규칙:

  • 모든 출력 검증은 insta::assert_snapshot! 사용. assert!(result.contains(...))
    방식은 금지 — 회귀 감지 정확도 확보 및 출력 전체를 고정.
  • 구조가 동일한 케이스(컬럼 타입 × nullable, default 값 변형, on_delete/on_update,
    singularize 등)는 #[rstest] 파라미터화로 작성.
  • 반복되는 ColumnDef 생성은 로컬 헬퍼(col, col_null, col_with_default,
    col_with_comment, pk, uniq, idx, fk, table, render_schema_all)로
    분리하여 케이스 본문을 짧게 유지.

테스트 65개 / 스냅샷 54개가 커버하는 범위:

Layer 항목
1-1 컬럼 타입 × nullable 25개 타입 (Simple + Complex, enum 포함) × {nullable=true, nullable=false}
1-2 PK 단일 autoinc / 단일 no-autoinc / composite / 없음
1-3 unique 단일 named / 단일 unnamed / composite named / composite unnamed
1-4 index 단일 named / 단일 unnamed / composite
1-5 default bool, now(), CURRENT_TIMESTAMP, gen_random_uuid(), uuid_generate_v4(), 임의 함수, 문자열 리터럴, 정수 리터럴, fallback 키워드
1-6 enum screaming_snake match, mapped, integer, nullable, enum default, 다중 컬럼 dedup
1-7 description / comment 있음 / 없음 / multiline / 컬럼 comment multiline
1-8 @@Map 항상 출력
2 관계 has-many, has-one (unique FK), nullable FK, 같은 테이블로의 다중 FK, 자기참조, on_delete/on_update (6 action), composite FK 무시, 복수형→단수형 back-relation (posts/categories/boxes/users)
3 edge 예약어 테이블명 (select, order, model, ...), 예약어 컬럼명 (default, unique, ...), description 안의 newline/quote/brace, default 값 안의 quote

Test plan

  • cargo build -p vespertide-exporter — 정상
  • cargo test -p vespertide-exporter --lib prisma:: — 65 passed
  • cargo test -p vespertide-exporter orm:: — 207 passed (Prisma dispatch 스냅샷 포함)
  • 리뷰어: examples/app/cargo run -p vespertide-cli -- export --orm prisma 실행해 동작 확인
  • 리뷰어 (선택): 생성된 출력에 npx prisma format / npx prisma validate 통과 여부 확인

🤖 Generated with Claude Code

L33gn21 and others added 2 commits May 23, 2026 18:31
Add Prisma as a fifth ORM backend that converts `TableDef` schemas
into a single `schema.prisma` file (datasource, generator, enums, models).

Why Prisma is wired differently from the other backends:

- Prisma is a single-schema language: datasource (provider, url,
  relationMode) and generator (client output) must live in the same
  file as the models. Other ORMs only emit model code per file, so
  they need no such meta-config. A dedicated `PrismaConfig`
  (provider / client_output / relation_mode) is therefore added to
  `vespertide-config`.

- The existing export pipeline is N models -> N files (walk_models ->
  per-model render -> per-file write -> mod chain). Prisma is N
  models -> 1 file with globally-deduplicated enum blocks and a
  single datasource/generator header. `cmd_export` short-circuits
  with `if matches!(orm, OrmArg::Prisma) { return cmd_export_prisma }`
  and assembles the whole schema via `PrismaExporterWithConfig::render_schema`.

Test design (see `prisma/TESTING.md`):

- 3 layers - Layer 1: single-entity rendering (no FK); Layer 2:
  schema-aware rendering with relations; Layer 3: edge cases.
- All assertions use `insta::assert_snapshot!` (no `contains` checks)
  for precise regression detection.
- `rstest` parameterization for type x nullable matrix, default-value
  variants, on_delete/on_update actions, and singularize cases.
- Helpers (`col`, `col_null`, `col_with_default`, `pk`, `uniq`, `idx`,
  `fk`, `table`, `render_schema_all`) keep cases compact.
- 65 tests / 54 snapshots cover: column type x nullable (25 types),
  PK variants, single/composite unique, single/composite index,
  9 default-value forms, enums (string/integer/nullable/default/dedup),
  description and column comment, `@@map`, has-many, has-one (unique
  FK), nullable FK, multiple FKs to same table, self-reference,
  on_delete/on_update (6 actions), composite FK ignored, plural->
  singular back-relations, reserved-identifier table/column names,
  and special characters in descriptions/defaults.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
id Int @id
val Int @default(dbgenerated("nextval('my_seq')"))

@@map("items")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

차음부터 model Items로 쓰는 방식을 어떨까요?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

복수형 → 단수형 변환 로직은 제거했습니다.

일반적으로 DB는 snake_case, Prisma는 PascalCase를 사용하는데,
이 부분도 통일해서 @@Map() 사용을 없애는 방향이 좋을까요?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

불필요한 MD파일은 생성을 지양합니다

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제거했습니다

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants